#pragma once

#include <iostream>
using namespace std;

#include "Uses.h"

Uses::Uses(VarTable* vt,StmtTable *st,ProcTable* pt) { 
	myST = st;
	myVT = vt;
	myPT = pt;
};

void Uses::setUsesStmtUses(INDEX stmtNo, INDEX varIndex) {
	usesTableStmt.insert(std::make_pair<INDEX,INDEX>(stmtNo,varIndex));
	++usesCount;
}

void Uses::setUsesProcUses(INDEX procIndex, INDEX varIndex) {
	unordered_multimap<INDEX,set<INDEX>>::const_iterator got = usesTableProc.find(procIndex);
	if (got == usesTableProc.end()) {
		set<INDEX> newSet;
		newSet.insert(varIndex);
		usesTableProc.insert(std::make_pair<INDEX, set<INDEX>>(procIndex, newSet));
	} else {
		for (auto it = usesTableProc.begin(); it != usesTableProc.end(); ++it) 
		{
			if (it->first == procIndex) 
			{
				it->second.insert(varIndex);
			}
		}
	}
};

void Uses::printAllStmt() {
	cout << "Printing Uses Table" << endl;
	for (auto it = usesTableStmt.begin(); it != usesTableStmt.end(); ++it) {
		cout << it->first << " : " << it->second << endl;
	}
};


INDEX_SET Uses::getUsedIndexUses(INDEX stmtIndex) {
	INDEX_SET returnVal;
	for (auto it = usesTableStmt.begin(); it != usesTableStmt.end(); ++it) {
		if (it->first == stmtIndex) {
			returnVal.insert(it->second);
		}
	}
	return returnVal;
};

Schema Uses::getSynSyn(DESIGN_ENTITY de1, NAME de1Name, NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	attNames.push_back(de2Name);
	Schema x(2,attNames);
	if(de1=="procedure")
	{
		for (auto it = usesTableProc.begin(); it != usesTableProc.end(); ++it) {
			
			for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2)
			{
				vector<INDEX> vect;
				vect.push_back(it->first);
				vect.push_back(*it2);
				x.insertTuple(vect);
			}
			
		}
	}
	else
	{
		for (auto it = usesTableStmt.begin(); it != usesTableStmt.end(); ++it) {
			if ((myST->stmtTable.at(it->first) == de1 || de1 == "stmt")) {
				vector<INDEX> vect;
				vect.push_back(it->first);
				vect.push_back(it->second);
				x.insertTuple(vect);
			}
		}
	}
	return x;
};

Schema Uses::getSynIdent(DESIGN_ENTITY de1, NAME de1Name, string ident)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	Schema x(1,attNames);
	if(de1=="procedure")
	{
		for (auto it = usesTableProc.begin(); it != usesTableProc.end(); ++it) 
		{
			for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2)
			{
				vector<INDEX> vect;
				if (myVT->getVarName(*it2)==ident)
				{
					vect.push_back(it->first);
					x.insertTuple(vect);
				}
			}
			
		}
	}
	else
	{
	for (auto it = usesTableStmt.begin(); it != usesTableStmt.end(); ++it) {
		if ((myST->stmtTable.at(it->first) == de1 || de1 == "stmt") && myVT->getVarName(it->second)==ident) {
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
		}
	}}
	return x;
};

Schema Uses::getSynUnd(DESIGN_ENTITY de1, NAME de1Name)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	Schema x(1,attNames);
	if(de1=="procedure")
	{
		for (auto it = usesTableProc.begin(); it != usesTableProc.end(); ++it) {
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
		}
	}
	else
	{
		for (auto it = usesTableStmt.begin(); it != usesTableStmt.end(); ++it) {
			if ((myST->stmtTable.at(it->first) == de1 || de1 == "stmt")) {
				vector<INDEX> vect;
				vect.push_back(it->first);
				x.insertTuple(vect);
			}
		}
	}
	return x;
};

Schema Uses::getIntSyn(INDEX para1, NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de2Name);
	Schema x(1,attNames);
	for (auto it = usesTableStmt.begin(); it != usesTableStmt.end(); ++it) {
		if (it->first==para1) {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Uses::getIntIdent(INDEX para1, string para2)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = usesTableStmt.begin(); it != usesTableStmt.end(); ++it) {
		if ((it->first==para1) && (it->second==myVT->getVarIndex(para2))) {
			vector<INDEX> vect;
			vect.push_back(para1);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Uses::getIntUnd(INDEX para1)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = usesTableStmt.begin(); it != usesTableStmt.end(); ++it) {
		if (it->first==para1) {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Uses::getIdentIdent(string para1, string para2)
{
	int procIndex = myPT->getProcIndex(para1);
	int varIndex = myVT->getVarIndex(para2);
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = usesTableProc.begin(); it != usesTableProc.end(); ++it) {
		if ((it->first==procIndex)) 
		{
			for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2)
			{
				if(*it2==myVT->getVarIndex(para2))
				{
					vector<INDEX> vect;
					vect.push_back(procIndex);
					x.insertTuple(vect);
				}
			}
		}
	}
	return x;
};

Schema Uses::getIdentSyn(string para1, NAME de2Name)
{
	int procIndex = myPT->getProcIndex(para1);
	vector<NAME> attNames;
	attNames.push_back(de2Name);
	Schema x(1,attNames);
	for (auto it = usesTableProc.begin(); it != usesTableProc.end(); ++it) {
		if ((it->first==procIndex)) 
		{
			for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2)
			{
				vector<INDEX> vect;
				vect.push_back(*it2);
				x.insertTuple(vect);
			}
		}
	}
	return x;
};

Schema Uses::getIdentUnd(string para1)
{
	int procIndex = myPT->getProcIndex(para1);
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = usesTableProc.begin(); it != usesTableProc.end(); ++it) {
		if (it->first==procIndex) 
		{
			for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2)
			{
				vector<INDEX> vect;
				vect.push_back(*it2);
				x.insertTuple(vect);
			}
		}
	}
	return x;
};